#include <string>

#include "napi/native_api.h"
#include "./base64/base64.h"

static napi_value Add(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    return sum;
}

static napi_value Base64_Encode(napi_env env, napi_callback_info info) {
    // 1、从info中取出JS传递过来的参数放入args
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        napi_throw_error(env, "-1000", "napi_get_cb_info error");
        return nullptr;
    }

    // 2、获取参数的类型
    napi_valuetype stringType;
    if (napi_ok != napi_typeof(env, args[0], &stringType)) {
        napi_throw_error(env, "-1001", "napi_typeof error");
        return nullptr;
    }

    // 3、如果参数为null，则抛异常
    if (napi_null == stringType) {
        napi_throw_error(env, "-1002", "the param can't be null");
        return nullptr;
    }

    // 4、获取传递的string长度
    size_t length = 0;
    if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {
        napi_throw_error(env, "-1003", "napi_get_value_string_utf8 error");
        return nullptr;
    }

    // 5、如果传递的是""，则抛异常
    if (length == 0) {
        napi_throw_error(env, "-1004", "the param length invalid");
        return nullptr;
    }

    // 6、读取传递的string参数放入buffer中
    char *buffer = new char[length + 1];
    if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1005", "napi_get_value_string_utf8 error");
        return nullptr;
    }
    
    // 7、计算base64编码操作
    Base64 base64;
    std::string strEncode = buffer;
    strEncode = base64.Encode((const unsigned char*)strEncode.c_str(), strEncode.length());

    // 8、把C++数据转成napi_value
    napi_value value = nullptr;
    const char *encode = strEncode.c_str();
    if (napi_ok != napi_create_string_utf8(env, encode, strlen(encode), &value)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1006", "napi_create_string_utf8 error");
        return nullptr;
    }

    // 9、资源清理
    delete[] buffer;
    buffer = nullptr;

    // 返回 value
    return value;
}

static napi_value Base64_Decode(napi_env env, napi_callback_info info) {
    // 1、从info中取出JS传递过来的参数放入args
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
        napi_throw_error(env, "-1000", "napi_get_cb_info error");
        return nullptr;
    }

    // 2、获取参数的类型
    napi_valuetype stringType;
    if (napi_ok != napi_typeof(env, args[0], &stringType)) {
        napi_throw_error(env, "-1001", "napi_typeof error");
        return nullptr;
    }

    // 3、如果参数为null，则抛异常
    if (napi_null == stringType) {
        napi_throw_error(env, "-1002", "the param can't be null");
        return nullptr;
    }

    // 4、获取传递的string长度
    size_t length = 0;
    if (napi_ok != napi_get_value_string_utf8(env, args[0], nullptr, 0, &length)) {
        napi_throw_error(env, "-1003", "napi_get_value_string_utf8 error");
        return nullptr;
    }

    // 5、如果传递的是""，则抛异常
    if (length == 0) {
        napi_throw_error(env, "-1004", "the param length invalid");
        return nullptr;
    }

    // 6、读取传递的string参数放入buffer中
    char *buffer = new char[length + 1];
    if (napi_ok != napi_get_value_string_utf8(env, args[0], buffer, length + 1, &length)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1005", "napi_get_value_string_utf8 error");
        return nullptr;
    }
    
    // 7、计算base64解码操作
    Base64 base64;
    int outByte = -1;
    std::string strEncode = buffer;
    strEncode = base64.Decode(strEncode.c_str(), strEncode.length(), outByte);

    // 8、把C++数据转成napi_value
    napi_value value = nullptr;
    const char *encode = strEncode.c_str();
    if (napi_ok != napi_create_string_utf8(env, encode, strlen(encode), &value)) {
        delete[] buffer;
        buffer = nullptr;
        napi_throw_error(env, "-1006", "napi_create_string_utf8 error");
        return nullptr;
    }

    // 9、资源清理
    delete[] buffer;
    buffer = nullptr;

    // 返回 value
    return value;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "Base64_Encode", nullptr, Base64_Encode, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "Base64_Decode", nullptr, Base64_Decode, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}
